package com.suray.wcs.service.core.path.api.impl;

import com.google.common.collect.Lists;
import com.suray.basic.wcs.path.core.Node;
import com.suray.basic.wcs.path.core.Route;
import com.suray.basic.wcs.path.core.api.impl.DifFloorRouteFactoryImp;
import com.suray.basic.wcs.path.core.api.impl.RouteFactoryImp;
import com.suray.basic.wcs.path.enums.NodeStatus;
import com.suray.basic.wcs.path.enums.NodeType;
import com.suray.basic.wcs.time.WCSTimeModule;
import com.suray.basic.wcs.time.bo.WCSTimeModuleResultBO;
import com.suray.basic.wcs.utils.Coord;
import com.suray.commin.core.utils.SpringContextHolder;
import com.suray.wcs.res.enums.RgvStatus;
import com.suray.wcs.service.core.path.api.DeadlockHandle;
import com.suray.wcs.service.core.util.CommonUtil;
import com.suray.wcs.service.core.util.LockNodeManager;
import com.suray.wcs.service.enums.RoadType;
import com.suray.wcs.service.enums.TaskRgvProgress;
import com.suray.wcs.service.enums.TaskType;
import com.suray.wcs.service.execption.InterruptTaskThenAvoidException;
import com.suray.wcs.service.init.SystemInit;
import com.suray.wcs.service.pojo.Rgv;
import com.suray.wcs.service.system.entity.NodeDB;
import com.suray.wcs.service.system.entity.TaskDB;
import com.suray.wcs.service.system.service.impl.NodeDBServiceImpl;
import com.suray.wcs.service.system.service.impl.TaskDBServiceImpl;
import com.suray.wcs.service.thread.RgvExecuteThread;
import org.apache.commons.collections.CollectionUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Lazy;
import org.springframework.stereotype.Component;

import java.util.*;

@Lazy
@Component
public class DeadLockHandleImpl implements DeadlockHandle {
    private Logger log = LoggerFactory.getLogger(this.getClass());
    @Autowired
    private NodeDBServiceImpl nodeService;
    @Autowired
    private TaskDBServiceImpl taskDBService;

    /**
     * 绕路实现, 无路可绕返回false
     *
     * @return
     */
    @Autowired
    public DeadLockHandleImpl(NodeDBServiceImpl nodeService) {
        this.nodeService = nodeService;
    }

    @Override
    public synchronized boolean detour() {
        return false;
    }


    /**
     * 判断是否为死锁
     * 条件为两辆车相互因对方的资源锁而被迫停止
     *
     * @return
     */
    @Override
    public synchronized boolean isDeadlock() {
        Rgv curRgv = ((RgvExecuteThread) Thread.currentThread()).getRgv();
        //找到该车下一段路径中存在阻挡的车
        List<Rgv> rejectRgvSet = LockNodeManager.getNextRoadRejectRgvList(curRgv);

        boolean isDeadlock = false;
        for (Rgv rgv : rejectRgvSet) {
            TaskDB curTaskDB = curRgv.getCurTask();
            List<TaskDB> regTaskDBs = rgv.getWaitTasks();
            if ((curTaskDB != null && regTaskDBs != null && regTaskDBs.size() > 0) || CollectionUtils.isNotEmpty(rgv.getWaitSingleRoadRgvS())) {
                rgv.setWaitRgv(curRgv);
                //停止锁的判定(一辆车无路径执行时占用某一资源节点)
            } else if (rgv.getWaitExecuteNodeList() != null && LockNodeManager.getNextRoadRejectRgvList(rgv).contains(curRgv)) {
                curRgv.setRejectRgv(rgv);
                rgv.setRejectRgv(curRgv);
                log.info("出现死锁1: " + curRgv.getRgvDB().getRgvNo() + "与" + rgv.getRgvDB().getRgvNo());
                isDeadlock = true;
                break;
            } else if (rgv.getWaitExecuteNodeList() == null && rgv.getCurTask() == null && rgv.getRgvStatus() == RgvStatus.READY) {
                TaskDB taskDB = taskDBService.findFirstCanExecuteTask(rgv.getRgvDB().getRgvNo());
                if (taskDB == null) {
                    curRgv.setRejectRgv(rgv);
                    log.info("出现死锁2: " + curRgv.getRgvDB().getRgvNo() + "与" + rgv.getRgvDB().getRgvNo());
                    isDeadlock = true;
                    break;
                }
            } else if (rgv.getWaitConnectPointNoGoods() && !(rgv.getCurTask().getTaskType().equals(curRgv.getCurTask().getTaskType()) && rgv.getWaitExecuteNodeList() != null
                    && curRgv.getWaitExecuteNodeList().get(curRgv.getWaitExecuteNodeList().size() -1).equals(rgv.getWaitExecuteNodeList().get(rgv.getWaitExecuteNodeList().size() -1)))) {
                rgv.setWaitRgv(curRgv);
            }
        }
        if (!isDeadlock) {
            List<Rgv> resultRgvs = new ArrayList<>();
            List<Rgv> walkedRgvs = new ArrayList<>();
            walkedRgvs.add(curRgv);
            Rgv rejectRgv = LockNodeManager.getFontRejectRgv(curRgv);
            if (rejectRgv != null) {
                walkedRgvs.add(rejectRgv);
                rejectRgvSetContainRgv(rejectRgv, walkedRgvs, curRgv, resultRgvs);
                if (rejectRgv.equals(curRgv) || resultRgvs.size() > 0) {
                    Rgv course = curRgv;
                    for (int i = resultRgvs.size() - 1; i >= 0; i--) {
                        resultRgvs.get(i).setRejectRgv(course);
                        course = resultRgvs.get(i);
                    }
                    log.info("出现循环死锁: " + curRgv.getRgvDB().getRgvNo() + "与" + rejectRgv.getRgvDB().getRgvNo());
                    isDeadlock = true;
                }
            }
        }

        if (!isDeadlock) {
            //非死锁, 仅等待
            curRgv.setRejectStatus(Rgv.RejectStatus.WAIT);
        }
        return isDeadlock;
    }

    /**
     * 判断是否是循环死锁
     *
     * @param curRgv
     * @param walkedRgvs 当前链上的车
     * @return
     */
    private void rejectRgvSetContainRgv(Rgv curRgv, List<Rgv> walkedRgvs, Rgv rootRgv, List<Rgv> resultRgvs) {
        Rgv rejectRgv = LockNodeManager.getFontRejectRgv(curRgv);
        if (rejectRgv != null) {
            while (resultRgvs.size() < 1) {
                if (rootRgv.equals(rejectRgv)) {
                    //根节点的小车等待链表里有循环等待，并且根节点的小车在环里
                    resultRgvs.addAll(walkedRgvs);
                    return;
                } else if (walkedRgvs.contains(rejectRgv)) {
                    //根节点的小车等待链表里有循环等待
                    return;
                } else {
                    walkedRgvs.add(rejectRgv);
                    rejectRgv = LockNodeManager.getFontRejectRgv(rejectRgv);
                    if (rejectRgv == null) {
                        return;
                    }
                }
            }
        }
    }

    /**
     * 使入参小车执行避让,移动到某一位置
     *
     * @param rgv
     * @return 可避让返回true, 不可避让返回false
     */
    private boolean rgvAvoid(Rgv rgv, Node node) {
        TaskDBServiceImpl taskService = SpringContextHolder.getBean(TaskDBServiceImpl.class);
        TaskDB taskDB = taskDBService.findFirstCanExecuteTask(rgv.getRgvDB().getRgvNo());
        if (taskDB == null && rgv.getRgvStatus().equals(RgvStatus.READY)) {
            TaskDB task = new TaskDB();
            task.setEndNode(node);
            if (rgv.isHasCargo()) {
                task.setTaskType(TaskType.MOVE_LOADED);
            } else {
                task.setTaskType(TaskType.MOVE_UNLOADED);
            }
            task.setProcessingRgvNo(rgv.getRgvDB().getRgvNo());
            task.setPriorityCode(CommonUtil.PRIORITY_CODE_AVOID);
            taskService.insertTask(Collections.singletonList(task));
            log.info("避让车辆" + rgv + "避让点" + node);
            return true;
        } else if (rgv.getRejectRgv() != null || rgv.getWaitRgv() != null) {
            //重新规划路径
            log.info("避让开始，为小车生成避让任务");
            TaskDB curTaskDB = rgv.getCurTask();

            if (curTaskDB != null && curTaskDB.getPriorityCode() >= Integer.MAX_VALUE) {
                throw new IllegalStateException("此任务优先级最高，系统不处理排斥死锁, 请人工处理");
            }
            TaskType taskType = null;
            if (rgv.isHasCargo()) {
                taskType = TaskType.MOVE_LOADED;
            } else {
                taskType = TaskType.MOVE_UNLOADED;
            }
            List<TaskDB> taskDBList = taskDBService.findUnFinishAvoidTaskByEndNodeStrAndTaskType(node.toString(), rgv.getRgvDB().getRgvNo(), taskType);
            if (CollectionUtils.isEmpty(taskDBList)) {
                TaskDB avoidTask = new TaskDB();
                avoidTask.setEndNode(node);
                avoidTask.setTaskType(taskType);
                avoidTask.setPriorityCode(CommonUtil.PRIORITY_CODE_AVOID);//避让任务优先级
                avoidTask.setProcessingRgvNo(rgv.getRgvDB().getRgvNo());
                avoidTask.setRgvProgress(TaskRgvProgress.ASSIGNED_TO_RGV);
                taskService.insertTask(Collections.singletonList(avoidTask));
            }
            log.info("避让车辆" + rgv + "避让点" + node);
            rgv.setRejectRgv(null);
            rgv.setWaitRgv(null);
            throw new InterruptTaskThenAvoidException("中断当前任务" + (curTaskDB != null ? curTaskDB.getTaskId() : ":未知") + ", 准备执行避让任务");
        }
        log.info("避让失败！");
        return true;
    }

    /**
     * 获取最佳避让点
     *
     * @param rejectRgv 当前车辆
     * @param avoidRgv  避让车辆
     * @return
     */
    @Override
    public Node getAvoidNode(Rgv rejectRgv, Rgv avoidRgv, boolean isWaitAvoid) {
        int nodeZ = avoidRgv.getCurLocation().getZ();
        NodeDB avoidRgvNodeDB = nodeService.getNodeDB(avoidRgv.getCurLocation());
        List<Coord> availableNodeList;
        if (avoidRgv.isHasCargo()) {
            if ((avoidRgvNodeDB.getGridType() == NodeType.R || avoidRgvNodeDB.getGridType() == NodeType.CONNECTION)) {
                availableNodeList = nodeService.findSpecifiedNodes(Arrays.asList(NodeType.R, NodeType.P), NodeStatus.N, nodeZ);
            } else {
                availableNodeList = nodeService.findSpecifiedNodes(NodeType.R, NodeStatus.N, nodeZ);
            }
        } else {
            availableNodeList = nodeService.findSpecifiedNodes(Arrays.asList(NodeType.R, NodeType.P), null, nodeZ);
        }
        List<Node> nodes = rejectRgv.getWaitExecuteNodeList();
        if (CollectionUtils.isEmpty(nodes)) {
            log.info(rejectRgv.getRgvName() + "路径队列为空");
        } else {
            for (Node node : nodes) {
                availableNodeList.removeAll(node.getAvoidLockedNodeSet());
            }
        }
        //如果是单向道，那么离开这个巷道
        if (avoidRgvNodeDB.getRoadType() != null && avoidRgvNodeDB.getRoadType().equals(RoadType.S.getVal())) {
            try {
                List<Node> avoidRgvRouteNodeList = nodeService.getNodeDBListByRoadNos(avoidRgvNodeDB);
                Iterator<Node> it = avoidRgvRouteNodeList.iterator();
                while (it.hasNext()) {
                    Node node = it.next();
                    if (!availableNodeList.contains(node)) {
                        it.remove();
                    }
                }
                availableNodeList.removeAll(avoidRgvRouteNodeList);
                log.info(avoidRgv.getRgvName() + "号车，去其他巷道进行避让！！！");
            } catch (Exception e) {
                e.printStackTrace();
                log.info(avoidRgv.getRgvName() + "去除单向道可避让点失败！");
            }
        }
        availableNodeList.removeAll(LockNodeManager.getLockedNodes());
        SystemInit.removeLockNode(availableNodeList);
        if (isWaitAvoid) {
            return getBestAvoidNode(availableNodeList, avoidRgv);
        } else {
            return getBestAvoidNode(availableNodeList, avoidRgv, Lists.newArrayList(nodeService.getLinkedPlusNodeSet(rejectRgv.getCurLocation())));
        }
    }

    /**
     * 查找出最优的避让节点
     * 同层最优点，不做跨层避让
     *
     * @param availableNodeList 所有的可用节点
     * @param rgv               需要避让的车辆
     * @return
     */
    @Override
    public Node getBestAvoidNode(List<Coord> availableNodeList, Rgv rgv) {
        Integer floor = rgv.getCurLocation().getZ();
        RouteFactoryImp routeFactoryImp = new RouteFactoryImp(nodeService.getNodeListByFloor(floor),
                LockNodeManager.getLockedNodesExceptSelf(rgv), rgv.generateWCSTimeModuleBO(), rgv.getRgvDB().getRgvType());
        Node bestNode = routeFactoryImp.getLatelyNode(rgv.getCurLocation(), availableNodeList,
                !rgv.isHasCargo() ? false : true);
        log.info("避让点/缓存点：" + bestNode);
        return bestNode;
    }

    /**
     * 查找出最优的避让节点
     * 同层最优点，不做跨层避让
     *
     * @param availableNodeList 所有的可用节点
     * @param rgv               需要避让的车辆
     * @return
     */
    @Override
    public Node getBestAvoidNode(List<Coord> availableNodeList, Rgv rgv, List<Coord> disableNodeList) {
        Integer floor = rgv.getCurLocation().getZ();
        List<Node> distNodes = nodeService.getNodeListByFloor(floor);
        for (Node distNode : distNodes) {
            for (Coord disableNode : disableNodeList) {
                if (distNode.equals(disableNode)) {
                    distNode.setUp(false);
                    distNode.setDown(false);
                    distNode.setLeft(false);
                    distNode.setRight(false);
                }
            }
        }
        RouteFactoryImp routeFactoryImp = new RouteFactoryImp(distNodes, LockNodeManager.getLockedNodes(),
                rgv.generateWCSTimeModuleBO(), rgv.getRgvDB().getRgvType());
        Node bestNode = routeFactoryImp.getLatelyNode(rgv.getCurLocation(), availableNodeList,
                !rgv.isHasCargo() ? false : true);
        log.info("避让点" + bestNode);
        return bestNode;
    }

    @Override
    public synchronized boolean avoid() {
        Rgv curRgv = ((RgvExecuteThread) Thread.currentThread()).getRgv();
        Rgv rejectRgv = curRgv.getRejectRgv();
        TaskDB rejectRgvTaskDB = taskDBService.findFirstCanExecuteTask(rejectRgv.getRgvDB().getRgvNo());
        if (rejectRgvTaskDB == null) {//其中一辆车处于静止状态
            Node avoidNode = getAvoidNode(curRgv, rejectRgv, false);
            if (avoidNode == null) {
                avoidNode = getAvoidNode(curRgv, rejectRgv, true);
            }
            if (avoidNode == null) {
                log.info("避让点为空,无法避让");
                return false;
            }
            if (rgvAvoid(rejectRgv, avoidNode)) {
                log.info("开始无任务避让");
                curRgv.setRejectRgv(null);
                rejectRgv.setRejectRgv(null);
                return true;
            } else {
                log.info(rejectRgv + "无任务避让失败");
                return false;
            }
            // 排斥需要让路的小车，让该小车避让
        } else if (rejectRgv.getWaitTasks() != null && rejectRgv.getWaitTasks().size() > 0) {
            rejectRgv.setWaitRgv(curRgv);
            return true;
        } else {
            Node avoidNode = getAvoidNode(rejectRgv, curRgv, false);
            // 如果只有一方可以避让，则让一方避让
            if (avoidNode != null) {
                // 如果当前小车身上任务不为空，1.小车进度为前往起点中，避让点为起点；2.小车进度为前往终点中，避让点为终点，则不需要再产生避让任务。
                if (curRgv.getCurTask() != null &&
                        ((TaskRgvProgress.RGV_TO_START.equals(curRgv.getCurTask().getRgvProgress()) && curRgv.getCurTask().getStartNodeStr() != null
                                && Node.parseGrid(curRgv.getCurTask().getStartNodeStr()).equals(avoidNode))
                                || (TaskRgvProgress.RGV_TO_END.equals(curRgv.getCurTask().getRgvProgress()) && curRgv.getCurTask().getEndNodeStr() != null
                                && Node.parseGrid(curRgv.getCurTask().getEndNodeStr()).equals(avoidNode)))) {
                    return true;
                }
                // 如果死锁小车身上任务不为空，且死锁车辆任务为避让任务，死锁车辆有行驶节点，且行驶节点包含当前车辆的位置时，当前车辆避让。
                if (rejectRgv.getCurTask() != null && CommonUtil.PRIORITY_CODE_AVOID.equals(rejectRgv.getCurTask().getPriorityCode())
                        && CollectionUtils.isNotEmpty(rejectRgv.getExecuteNodeList()) && rejectRgv.getExecuteNodeList().contains(curRgv.getCurLocation())) {
                    if (rgvAvoid(curRgv, avoidNode)) {
                        log.info(curRgv.getRgvName() + "清除了" + rejectRgv.getRgvName() + "与自己的排斥车辆");
                        return true;
                    } else {
                        log.info(curRgv + "避让失败");
                        return false;
                    }
                }
                // 如果双方都可以避让，则让时间计算消耗小的一方避让
                Node rejectRgvAvoidNode = getAvoidNode(curRgv, rejectRgv, false);
                if (rejectRgvAvoidNode != null) {
                    List<Integer> layers = new ArrayList<>();
                    layers.add(curRgv.getCurLocation().getZ());
                    DifFloorRouteFactoryImp curRgvRouteFactory =
                            new DifFloorRouteFactoryImp(nodeService.getNodeByLayer(layers),
                                    LockNodeManager.getLockedNodesExceptSelf(curRgv), curRgv.generateWCSTimeModuleBO(),
                                    curRgv.getRgvDB().getRgvType());
                    List<Route> toUnLoad = curRgvRouteFactory.createRoute(curRgv.getCurLocation(), avoidNode,
                            !curRgv.isHasCargo() ? false : true);
                    WCSTimeModuleResultBO<List<Route>> curRgvListWCSTimeModuleResultBO = WCSTimeModule.calculateRouteListPassTime(toUnLoad, curRgv.generateWCSTimeModuleBO(), curRgv.getRgvDB().getRgvNo());

                    DifFloorRouteFactoryImp rejectRgvRouteFactory =
                            new DifFloorRouteFactoryImp(nodeService.getNodeByLayer(layers),
                                    LockNodeManager.getLockedNodesExceptSelf(rejectRgv),
                                    rejectRgv.generateWCSTimeModuleBO(), curRgv.getRgvDB().getRgvType());
                    List<Route> rejectRgvtoUnLoad = rejectRgvRouteFactory.createRoute(rejectRgv.getCurLocation(), rejectRgvAvoidNode,
                            !rejectRgv.isHasCargo() ? false : true);
                    WCSTimeModuleResultBO<List<Route>> rejectRgvListWCSTimeModuleResultBO = WCSTimeModule.calculateRouteListPassTime(rejectRgvtoUnLoad, rejectRgv.generateWCSTimeModuleBO(), rejectRgv.getRgvDB().getRgvNo());
                    log.info("当前{}消耗：{},避让点：{},对方{}消耗：{},避让点：{}", curRgv.getRgvName(), curRgvListWCSTimeModuleResultBO.getTotalTime(), avoidNode, rejectRgv.getRgvName(), rejectRgvListWCSTimeModuleResultBO.getTotalTime(), rejectRgvAvoidNode);
                    if (curRgvListWCSTimeModuleResultBO.getTotalTime() > rejectRgvListWCSTimeModuleResultBO.getTotalTime()) {
                        return true;
                    } else {
                        // 避让消耗相同时，空车让载货
                        if (curRgvListWCSTimeModuleResultBO.getTotalTime() == rejectRgvListWCSTimeModuleResultBO.getTotalTime()) {
                            //当前车辆有货，死锁车辆无货
                            if (curRgv.isHasCargo() && !rejectRgv.isHasCargo()) {
                                return true;
                                //当前车辆无货，死锁车辆有货
                            } else if (!curRgv.isHasCargo() && rejectRgv.isHasCargo()) {
                                if (rgvAvoid(curRgv, avoidNode)) {
                                    log.info(curRgv.getRgvName() + "无货避让，" + rejectRgv.getRgvName() + "有货");
                                    return true;
                                } else {
                                    log.info(curRgv + "避让失败");
                                    return false;
                                }
                            } else {
                                // 如果都无货或有货，则车辆编号小的车辆避让
                                if (curRgv.getRgvDB().getRgvNo() > rejectRgv.getRgvDB().getRgvNo()) {
                                    return true;
                                } else {
                                    if (rgvAvoid(curRgv, avoidNode)) {
                                        log.info(curRgv.getRgvName() + "清除了" + rejectRgv.getRgvName() + "与自己的排斥车辆");
                                        return true;
                                    } else {
                                        log.info(curRgv + "避让失败");
                                        return false;
                                    }
                                }
                            }
                        } else {
                            if (rgvAvoid(curRgv, avoidNode)) {
                                log.info(curRgv.getRgvName() + "清除了" + rejectRgv.getRgvName() + "与自己的排斥车辆");
                                return true;
                            } else {
                                log.info(curRgv + "避让失败");
                                return false;
                            }
                        }
                    }
                } else {
                    if (rgvAvoid(curRgv, avoidNode)) {
                        log.info(curRgv.getRgvName() + "清除了" + rejectRgv.getRgvName() + "与自己的排斥车辆");
                        return true;
                    } else {
                        log.info(curRgv + "避让失败");
                        return false;
                    }
                }
            } else {
                avoidNode = getAvoidNode(curRgv, rejectRgv, false);
                if (avoidNode != null) {
                    return true;
                }
            }
            log.info(curRgv.getRgvName() + "与" + rejectRgv.getRgvName() + "都无法避让");
            return false;
        }
    }


    @Override
    public boolean waitAvoid() {
        Rgv curRgv = ((RgvExecuteThread) Thread.currentThread()).getRgv();
        Rgv rejectRgv = curRgv.getWaitRgv();
        if (rejectRgv != null) {
            Node avoidNode = getAvoidNode(rejectRgv, curRgv, false);
            if (avoidNode == null) {
                avoidNode = getAvoidNode(rejectRgv, curRgv, true);
            }
            if (avoidNode == null) {
                log.info("避让点为空,无法避让");
                return false;
            }
            if (rgvAvoid(curRgv, avoidNode)) {
                log.info("开始无任务避让");
                curRgv.setWaitRgv(null);
                return true;
            } else {
                log.info(rejectRgv + "无任务避让失败");
                return false;
            }
        }
        return false;
    }

}
